/**
 * Copyright 厦门中软海晟信息技术有限公司 版权所有 违者必究 2019
 */
package com.opencvjava.piano.support;

import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

import static com.opencvjava.support.util.CvUtils.imshow;
import static com.opencvjava.support.util.CvUtils.mat;
import static org.opencv.imgproc.Imgproc.CV_SHAPE_RECT;
import static org.opencv.imgproc.Imgproc.MORPH_CLOSE;
import static org.opencv.imgproc.Imgproc.MORPH_OPEN;
import static org.opencv.imgproc.Imgproc.MORPH_TOPHAT;
import static org.opencv.imgproc.Imgproc.getStructuringElement;
import static org.opencv.imgproc.Imgproc.morphologyEx;

/**
 * @author : sunzb
 * @date: 2019/11/27
 */
@Service
public class SourceMat {

    private final Logger logger = LoggerFactory.getLogger(SourceMat.class);
    private Mat src, gray, bin;
    private byte[] data, dataClose, dataNoLine, dataLine;
    private List<Integer> lineRowIndexes = new ArrayList<>();
    private int rows, cols, total, rowIndex, binThreshold = 0,
            lineLengthThreshold, seekLineRowSpan = 3;

    public void read(String filename) {
        src = mat("piano", filename);
        if (src.empty()) throw new RuntimeException("获取不到图片");
        gray = new Mat();
        Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
        if (gray.total() < 10000) throw new RuntimeException("图片尺寸太小");
        if (gray.total() > 20000000) throw new RuntimeException("图片尺寸太大");
        rows = gray.rows();
        cols = gray.cols();
        lineLengthThreshold = cols / 2;
        total = (int) gray.total();
        data = new byte[total];
        gray.get(0, 0, data);
    }

    public void verify() {
        while (!hasLine()) {
            // 进行校正
            throw new RuntimeException("找不到谱线");
        }
    }

    public void process() {
        dataNoLine = new byte[total];
        dataLine = new byte[total];
        // 二值化
        int color;
        for (int i = 0; i < total; i++) {
            color = data[i] & 0xff;
            if (color > binThreshold) data[i] = 0;
            else data[i] = -1;
        }
        bin = new Mat(gray.size(), gray.type());
        bin.put(0, 0, data);
        Mat element = getStructuringElement(CV_SHAPE_RECT, new Size(1, 5));
        Mat close = new Mat();
        morphologyEx(bin, close, MORPH_TOPHAT, element);
        imshow("顶帽", close);
        /*dataClose = new byte[total];
        close.get(0, 0, dataClose);
        for (int i = 0; i < total; i++)
            dataClose[i] = (byte) ((data[i] & 0xff) - (dataClose[i] & 0xff) & 0xff);
        close.put(0, 0, dataClose);
        imshow("闭操作差集", close);*/
        // 寻找细化谱线
        int dataNoLineRow = 0, dataNoLineRowIndex, dataLineRow = 0, dataLineRowIndex;
        for (int row = 0; row < rows; row++) {
            dataNoLineRowIndex = dataNoLineRow * cols;
            dataLineRowIndex = dataLineRow * cols;
            rowIndex = row * cols;
            if (isLine(rowIndex)) {
                lineRowIndexes.add(dataLineRow);
                for (int col = 0; col < cols; col++) dataLine[dataLineRowIndex + col] = 100;
                dataLineRow++;
                for (int row1 = row + 1; row1 < rows; row1++) {
                    if (!isLine(row1 * cols)) {
                        row = row1;
                        break;
                    }
                }
            } else {
                for (int col = 0; col < cols; col++) dataNoLine[dataNoLineRowIndex + col] = data[rowIndex + col];
                for (int col = 0; col < cols; col++) dataLine[dataLineRowIndex + col] = data[rowIndex + col];
                dataNoLineRow++;
                dataLineRow++;
            }
        }
    }

    private boolean isLine(int rowIndex) {
        int count = 0;
        for (int col = 0; col < cols; col++) if (data[rowIndex + col] == -1) count++;
        return count > lineLengthThreshold;
    }

    private boolean hasLine() {
        int iteNum = rows / seekLineRowSpan;
        int lineCount = 0;
        for (int i = 0; i < iteNum; i++) {
            int count = 0, color;
            for (int col = 0; col < cols; col++)
                for (int row = i; row < i + seekLineRowSpan; row++) {
                    color = data[row * cols + col] & 0xff;
                    if (color < 127) {
                        if (color > binThreshold) binThreshold = color;
                        count++;
                    }
                }
            if (count > lineLengthThreshold && ++lineCount == 10) return true;
        }
        return false;
    }

    public byte[] getData() {
        return data;
    }

    public byte[] getDataNoLine() {
        return dataNoLine;
    }

    public byte[] getDataLine() {
        return dataLine;
    }

    public List<Integer> getLineRowIndexes() {
        return lineRowIndexes;
    }

    public Mat getSrc() {
        return src;
    }
}
